package com.kaguya.singleton;

/**
 * @author : kaguya
 * 单例模式学习 懒汉模式
 * =================
 * 单例模式需要将实例 私有化
 * 然后需要一个可被全局访问的点 -》getInstance（）方法
 * 其他类需要使用该类时，只有调用getInstance（）方法才可实例化对象
 * @date : 16:35 2021/6/8
 */
public class LazySingletonTest {

    public static void main(String[] args) {
        Dog dog1 = Dog.getInstance();
        Dog dog2 = Dog.getInstance();
        //比较二者的hashcode 结果相同表明是同一个对象 单例模式实现
        System.out.println(dog1.hashCode() == dog2.hashCode());

        //虽然实现了，但是在多线程的情况下失效了，所以需要增加synchronized 进行同步
        //但是增加了synchronized会增加开销 消耗性能，所以可以采用双重检查锁的方式 在对象需要实例化的时候才加锁
        //在对象需要实例化的时候进行synchronized加锁，在属性前加上volatile关键字

        //对象在初始化时经过步骤：1分配内存空间 2初始化对象 3将对象指向刚分配的内存空间
        //有的编译器会进行指令重排，volatile 关键字的作用就是避免重排序
        //在单线程时 2 3顺序颠倒没有关系，多线程并发时，有时会出现引用赋值给一个尚未初始化的对象 NullPointer
        System.out.println("-------------------");

        new Thread(() -> {
            Dog dog = Dog.getInstance();
            System.out.println(dog.hashCode());
        }).start();

        new Thread(() -> {
            Dog dog = Dog.getInstance();
            System.out.println(dog.hashCode());
        }).start();
    }

    /**
     * 静态化属性 静态化构造方法，其他类通过定义的静态公共方法来实现实例化操作
     */
    public static class Dog {


        //私有化实例，其他类无法调用并实例化
        private volatile static Dog instance;

        //无参构造，私有化
        private Dog() {

        }

        /**
         * 当调用此方法时，才会实例化对象
         * if判断，只有当此对象是null时，才会创建，也就保证了 单例的实现：
         * 即，全局中只有这一个实例
         *
         * @return Dog
         */
        public static Dog getInstance() {
            //如果未被实例化 实例化对象
            if (null == instance) {
                synchronized (Dog.class) {
                    //双重检查
                    if (null == instance) {
                        instance = new Dog();
                    }
                }
            }
            return instance;
        }

    }
}
